﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using AbstraX.ServerInterfaces;
using System.ServiceModel.DomainServices.Server;
using System.ComponentModel.DataAnnotations;
using AbstraX.XPathBuilder;
using System.Runtime.Serialization;
using MvvmTreeView;
using System.Diagnostics;
using AbstraX.Templates;
using AbstraX;
using XPathParser;

namespace XmlSchemaProvider.Web.Entities
{
    [DataContract, NodeImage("XmlSchemaProvider.Web.Images.Attribute.png"), DebuggerDisplay("{ DebugInfo }"), ClientCodeGeneration(typeof(AbstraXClientInterfaceGenerator))]
    public class SchemaAttribute : SchemaBase, IAttribute
    {
        private string name;
        private XmlNode node;
        private string queryWhereProperty;
        private object queryWhereValue;
        private ISchemaParent parent;
        private string xPathExpression;

        public SchemaAttribute()
        {
        }

        public SchemaAttribute(XmlDocument schemaDocument, XmlNamespaceManager namespaceManager, string name, XmlNode node, ISchemaParent parent)
        {
            this.SchemaDocument = schemaDocument;
            this.NamespaceManager = namespaceManager;
            this.name = name;
            this.node = node;
            this.parent = parent;

            xPathExpression = this.GetLocation();
        }

        [Exclude()]
        public XmlNode Node
        {
            get 
            { 
                return node; 
            }
        }

        public IEnumerable<IAttribute> Attributes
        {
            get 
            {
                return null;
            }
        }

        public IEnumerable<IElement> ChildElements
        {
            get 
            {
                return null;
            }
        }

        public IEnumerable<IOperation> Operations
        {
            get { return null; }
        }

        [DataMember, Key]
        public string ID
        {
            get
            {
                return this.MakeID("Entity='" + name + "'");
            }
        }

        [DataMember]
        public string ParentID
        {
            get
            {
                return parent.ID;
            }
        }

        [DataMember]
        public string Name
        {
            get 
            {
                return name;
            }
        }

        [DataMember]
        public string ImageURL
        {
            get
            {
                return string.Empty;
            }
        }

        [DataMember]
        public bool IsContainer
        {
            get
            {
                return false;
            }
        }

        [Include, Association("Parent_BaseType", "ID", "ParentID")]
        public ScalarType DataType
        {
            get 
            {
                string _namespace = null;
                string suggestedNamespace = null;
                var parent = this.Parent;

                while (parent != null)
                {
                    if (parent is Project)
                    {
                        var project = (Project)parent;
                        _namespace = project.DefaultNamespace;

                        break;
                    }
                    else if (parent is Schema)
                    {
                        suggestedNamespace = ((Schema)parent).SchemaDocument.ChildNodes.Cast<XmlNode>().Single(e => e.LocalName == "schema").Attributes.Cast<XmlAttribute>().Single(a => a.Name == "targetNamespace").Value;
                    }

                    parent = parent.Parent;
                }

                var uri = new Uri(suggestedNamespace);
                string type = string.Empty;

                if (node is XmlAttribute)
                {
                    var attribute = ((XmlAttribute)node);

                    type = attribute.Value.Replace(attribute.OwnerElement.Prefix + ":", "");
                }
                else if (node.Attributes.GetNamedItem("type") != null)
                {
                    type = node.Attributes.GetNamedItem("type").Value.Replace(node.Prefix + ":", "");
                }

                suggestedNamespace = uri.Host;

                uri.Segments.Where(s => s.Replace("/", "").Length > 0).ToList<string>().ForEach(s => suggestedNamespace += "." + s.Replace("/", string.Empty).Replace(".xsd", ""));

                var dataType = new ScalarType
                {
                    ID = Guid.NewGuid().ToString(),
                    ParentID = this.ParentID,
                    Name = type,
                    FullyQualifiedName = type,
                    SuggestedNamespace = suggestedNamespace,
                };

                return dataType;
            }
        }

        public void ExecuteWhere(string property, object value)
        {
            if (value is XPathAxisElement && (property == "ID" || property == "ParentID"))
            {
                var predicate = ((XPathAxisElement)value).Predicates.First();

                queryWhereProperty = predicate.Left;
                queryWhereValue = predicate.Right;
            }
            else
            {
                System.Diagnostics.Debugger.Break();

                queryWhereProperty = property;
                queryWhereValue = value;
            }
        }

        public void ExecuteWhere(System.Linq.Expressions.Expression expression)
        {
        }

        public void ExecuteWhere(XPathAxisElement element)
        {
            var predicate = element.Predicates.First();

            queryWhereProperty = predicate.Left;
            queryWhereValue = predicate.Right;
        }

        public void ClearPredicates()
        {
            queryWhereProperty = null;
            queryWhereValue = null;
        }

        public IRoot Root
        {
            get
            {
                IBase baseObject = this;

                while (baseObject != null)
                {
                    baseObject = baseObject.Parent;

                    if (baseObject is IRoot)
                    {
                        return (IRoot)baseObject;
                    }
                }

                return null;
            }
        }


        [DataMember]
        public string Description
        {
            get { return string.Empty; }
        }

        [DataMember]
        public string Documentation
        {
            get { return string.Empty; }
        }

        [DataMember]
        public float ChildOrdinal
        {
            get
            {
                return 0;
            }
        }

        [DataMember]
        public string DebugInfo
        {
            get
            {
                return name + ":" + ID;
            }
        }

        public IBase Parent
        {
            get
            {
                return this.parent;
            }
        }

        [DataMember]
        public string FolderKeyPair { get; set; }

        [DataMember]
        public DefinitionKind Kind
        {
            get
            {
                return DefinitionKind.NotApplicable;
            }
        }

        [DataMember]
        public bool HasChildren
        {
            get
            {
                return false;
            }
        }

        [DataMember, Include, Association("Parent_Facet", "ID", "ParentID")]
        public Facet[] Facets
        {
            get
            {
                var id = Guid.NewGuid().ToString();

                var facets = new List<Facet>()
                {
                    new Facet
                    {
                        ID = id,
                        ParentID = this.ID,
                        FacetType = new BaseType
                        {
                            ID = Guid.NewGuid().ToString(),
                            ParentID = id,
                            Name = typeof(XPathAttribute).Name,
                            FullyQualifiedName = typeof(XPathAttribute).AssemblyQualifiedName
                        },
                        Arguments = new Argument[]
                        {
                            new Argument 
                            {
                                ID = Guid.NewGuid().ToString(),
                                ParentID = id,
                                Value = "\"" + xPathExpression + "\""
                            }
                        } 
                    }
                };

                if (node is XmlAttribute)
                {
                    var attribute = (XmlAttribute)node;
                    var ownerElement = attribute.OwnerElement;

                    if (ownerElement.LocalName == "restriction")
                    {
                        // add any restrictions

                        var baseType = ownerElement.Attributes.Cast<XmlAttribute>().Where(a => a.LocalName == "base").Select(a => a.Value).SingleOrDefault();

                        if (!string.IsNullOrEmpty(baseType))
                        {
                            var pattern = ownerElement.ChildNodes.Cast<XmlNode>().Where(n => n.LocalName == "pattern")
                                .Cast<XmlElement>().Select(e => e.Attributes["value"].Value).SingleOrDefault();

                            if (!string.IsNullOrEmpty(pattern))
                            {
                                // pattern restriction

                                id = Guid.NewGuid().ToString();

                                facets.Add(new Facet
                                {
                                    ID = id,
                                    ParentID = this.ID,
                                    FacetType = new BaseType
                                    {
                                        ID = Guid.NewGuid().ToString(),
                                        ParentID = id,
                                        Name = typeof(RestrictionPatternAttribute).Name,
                                        FullyQualifiedName = typeof(RestrictionPatternAttribute).AssemblyQualifiedName
                                    },
                                    Arguments = new Argument[] 
                                    { 
                                        new Argument 
                                        {
                                            ID = Guid.NewGuid().ToString(),
                                            ParentID = id,
                                            Value = "@\"" + pattern + "\""
                                        },
                                        new Argument 
                                        {
                                            ID = Guid.NewGuid().ToString(),
                                            ParentID = id,
                                            Value = "@\"" + baseType + "\""
                                        }
                                    }
                                });
                            }
                            else
                            {
                                // enumerations

                                var enumerations = ownerElement.ChildNodes.Cast<XmlNode>().Where(n => n.LocalName == "enumeration")
                                    .Cast<XmlElement>().Select(e => e.Attributes["value"].Value);

                                if (enumerations.Count() > 0)
                                {
                                    id = Guid.NewGuid().ToString();

                                    facets.Add(new Facet
                                    {
                                        ID = id,
                                        ParentID = this.ID,
                                        FacetType = new BaseType
                                        {
                                            ID = Guid.NewGuid().ToString(),
                                            ParentID = id,
                                            Name = typeof(RestrictionEnumerationAttribute).Name,
                                            FullyQualifiedName = typeof(RestrictionEnumerationAttribute).AssemblyQualifiedName
                                        },
                                        Arguments = enumerations.Select(e => new Argument
                                        {
                                            ID = Guid.NewGuid().ToString(),
                                            ParentID = id,
                                            Value = "\"" + e + "\""
                                        }).ToArray<Argument>()
                                    });
                                }
                            }
                        }
                    }
                }
                else if (node is XmlElement && node.Attributes.Cast<XmlAttribute>().Any(a => a.LocalName == "use"))
                {
                    // use

                    var use = Enum.Parse(typeof(Use), node.Attributes.Cast<XmlAttribute>().Single(a => a.LocalName == "use").Value, true);
                    id = Guid.NewGuid().ToString();

                    facets.Add(new Facet
                    {
                        ID = id,
                        ParentID = this.ID,
                        FacetType = new BaseType
                        {
                            ID = Guid.NewGuid().ToString(),
                            ParentID = id,
                            Name = typeof(UseAttribute).Name,
                            FullyQualifiedName = typeof(UseAttribute).AssemblyQualifiedName
                        },
                        Arguments = new Argument[] 
                            { 
                                new Argument 
                                {
                                    ID = Guid.NewGuid().ToString(),
                                    ParentID = id,
                                    Value = typeof(Use).FullName + "." + Enum.GetName(typeof(Use), use)
                                } 
                            }
                    });
                }

                return facets.ToArray<Facet>();
            }
        }

        [DataMember]
        public string XPathExpression
        {
            get
            {
                return xPathExpression;
            }
        }
    }
}
